package com.happe.foodeasy.winter.helper;

import com.happe.foodeasy.helper.AnnotationHelper;
import com.happe.foodeasy.helper.ClassOperateHelper;
import com.happe.foodeasy.helper.DaoHelper;
import com.happe.foodeasy.helper.annotation.FatherEntity;
import com.happe.foodeasy.winter.conf.parser.DatabaseTypeMapParseResult;
import com.happe.foodeasy.winter.conf.parser.DatabaseTypeParseTemplate;
import com.happe.foodeasy.winter.type.constraint.TypeConstraintHelper;
import com.happe.foodeasy.winter.typemap.ParseTypeMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.List;

/**
 * This class is used to generate sql expression for winter module.
 * Created by wubin on 2016-05-23.
 */
@Component(value = "winterSqlHelper")
public class WinterSqlHelper {
    private static final String SYSTEM_SEPARATOR = System.getProperty("line.separator", "\n");
    @Autowired
    private DatabaseTypeParseTemplate databaseTypeParserTemplate;
    @Autowired
    private ParseTypeMap parseTypeMap;
    @Autowired
    private TypeConstraintHelper typeConstraintHelper;

    /**
     * This method is used to generate create sql for database. depend on the target class object,
     * use the object's property and property type and the annotation of property to generate create sql.
     *
     * @param targetClass the target class that should generate the create sql.
     * @param <T>         the target class' type.
     * @return the create sql.
     */
    public <T> String generateCreateTableSql(Class<T> targetClass) {
        DaoHelper<T> daoHelper = new DaoHelper<>();
        String columnAndType = generateColumnAndType(targetClass);
        return "create table " + daoHelper.parseTableName(targetClass) + "(" + columnAndType.substring(0, columnAndType.lastIndexOf(",") - 1) + ");";
    }

    /*
    * This method is used to generate the content between parentheses of create table's sql.
    * */
    private <T> String generateColumnAndType(Class<T> targetClass) {
        String params = "";
        params += checkSuperClass(targetClass);
        Field[] allFieldsInTargetClass = ClassOperateHelper.retrieveFieldsBelongProperty(targetClass);
        for (Field oneFiled : allFieldsInTargetClass) {
            params += generateOneTableFieldDefinition(oneFiled);
        }

        return params;
    }

    /*
    * This method is used to generate one column of create table's sql by target field.
    * */
    private String generateOneTableFieldDefinition(Field oneField) {
        String fieldName = generateFieldName(oneField);
        String fieldType = generateFieldType(oneField);
        List<String> oneFieldConstraints = generateFieldIntegrityConstraint(oneField);

        String oneLineFieldSql = fieldName + " " + fieldType + " ";
        for (String oneConstraint : oneFieldConstraints) {
            oneLineFieldSql += oneConstraint + " ";
        }
        return oneLineFieldSql + ", " + SYSTEM_SEPARATOR;
    }

    private String generateFieldName(Field oneField) {
        boolean isForeignKey = AnnotationHelper.checkRelationalAnnotationForField(oneField);
        return isForeignKey ? oneField.getName() + "Id" : oneField.getName();
    }

    private String generateFieldType(Field oneField) {
        DatabaseTypeMapParseResult typeMapParseResult = databaseTypeParserTemplate.retrieveMapResult();
        return parseTypeMap.startTypeMap(oneField, typeMapParseResult);
    }

    /*
    * This method is used to generate the integrity constraints of target field,
    * note: the return type is List, because of one field can have more than one
    * integrity constraint.
    * */
    private List<String> generateFieldIntegrityConstraint(Field oneField) {
        return typeConstraintHelper.parseTargetFieldConstraint(oneField);
    }

    private <T> String checkSuperClass(Class<T> targetClass) {
        Class<? super T> superClass = targetClass.getSuperclass();
        if (superClass.isAnnotationPresent(FatherEntity.class)) {
            //this method's invoke means generate superClass's column and type first.
            return generateColumnAndType(superClass);
        }
        return "";
    }
}